package com.bes.bessdk.service.base;

import static com.bes.bessdk.BesSdkConstants.BES_CONNECT_SUCCESS;
import static com.bes.bessdk.BesSdkConstants.BES_NOTI_MSG_TIMEOUT_TOTA_START;
import static com.bes.bessdk.BesSdkConstants.BES_SAVE_LOG_OTA;
import static com.bes.bessdk.BesSdkConstants.BesConnectState;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONNECT;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONNECT_TOTA;
import static com.bes.bessdk.BesSdkConstants.MSG_TIME_OUT;
import static com.bes.sdk.utils.DeviceProtocol.PROTOCOL_BLE;
import static com.bes.sdk.utils.DeviceProtocol.PROTOCOL_USB;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import com.bes.bessdk.BesSdkConstants;
import com.bes.bessdk.connect.BTService;
import com.bes.bessdk.connect.BleConnector;
import com.bes.bessdk.connect.SppConnector;
import com.bes.bessdk.connect.UsbConnector;
import com.bes.bessdk.scan.BtHeleper;
import com.bes.bessdk.utils.ArrayUtil;
import com.bes.bessdk.utils.LogUtils;
import com.bes.bessdk.utils.SPHelper;
import com.bes.sdk.connect.DeviceConnector;
import com.bes.sdk.device.HmDevice;
import com.bes.sdk.message.BaseMessage;
import com.bes.sdk.utils.DeviceProtocol;

public class BesBaseService implements DeviceConnector.ConnectionListener, BesBaseConnectListener {
    static byte B_9F = (byte) 0x9F;
    public final String TAG = "BesBaseService";
    protected final HandlerThread mHandlerThread;
    private final Handler mMsgHandler;
    public BesServiceConfig mConfig;
    public DeviceConnector mDeviceConnector;
    public Context mContext;
    public boolean totauccess = false;
    public TOTAConnectCMD mTotaConnectCMD;
    public boolean isConnect = false;
    public boolean userOtaService = false;
    BesServiceListener mBesServiceListener;
    BBServiceListener mBBServiceListener;

    {
        mHandlerThread = new HandlerThread("BESHandler");
        mHandlerThread.start();
        mMsgHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                Log.i(TAG, "mMsgHandler: ---------");
                switch (msg.what) {
                    case MSG_TIME_OUT:
                        LOG(TAG, "handleMessage: ------MSG_TIME_OUT");
                        callBackErrorMessage(MSG_TIME_OUT);
                        break;
                    case BES_NOTI_MSG_TIMEOUT_TOTA_START:
                        LOG(TAG, "handleMessage: ------BES_NOTI_MSG_TIMEOUT_TOTA_START");
                        totaConnectFail();
                        break;

                    default:
                        break;
                }
            }
        };
    }

    public BesBaseService(BesServiceConfig serviceConfig, BesServiceListener listener, Context context) {
        mContext = context;
        mConfig = serviceConfig;
        if (mConfig.getTotaConnect()) {
            mTotaConnectCMD = new TOTAConnectCMD();
            if (getDeviceConnectState(mConfig) == BES_CONNECT) {
                mTotaConnectCMD.setAes_key(BTService.getTotaAesKey(mContext, mConfig.getDeviceProtocol(), getCurDevice()));
            }
        } else {
            mTotaConnectCMD = null;
        }
        userOtaService = serviceConfig.getIsUserOtaService();
        if (mContext == null || mConfig.getDeviceProtocol() == null || mConfig.getDevice() == null) {
            return;
        }
        mBesServiceListener = listener;
    }

    // add by wangsm
    public void setBesServiceListener(BesServiceListener besServiceListener) {
        this.mBesServiceListener = besServiceListener;
    }

    public void setBBServiceListener(BBServiceListener bbServiceListener) {
        this.mBBServiceListener = bbServiceListener;
    }

    public BesConnectState getDeviceConnectState(BesServiceConfig serviceConfig) {
        return BTService.getDeviceConnectState(mContext, serviceConfig);
    }

    public int getDeviceConnectState() {
        int bleConnStatus = -1;
        if (mConfig != null && mContext != null) {
            BluetoothManager btManager = BtHeleper.getBluetoothManager(mContext);// BTService.getDeviceConnectStateBB(this,
            // mServiceConfig);
            BluetoothDevice btDevice = getCurDevice();
            bleConnStatus = btManager.getConnectionState(btDevice, BluetoothProfile.GATT);
            Log.d(TAG, "wangsm_bes, getDeviceConnectState, bleConnStatus: " + bleConnStatus);
        }
        return bleConnStatus;
    }

    private BluetoothDevice getCurDevice() {
        if (mConfig.getDeviceProtocol() == PROTOCOL_USB) {
            return null;
        }
        HmDevice hmDevice = mConfig.getDevice();
        String address = mConfig.getDeviceProtocol() == PROTOCOL_BLE ? hmDevice.getBleAddress() : hmDevice.getDeviceMAC();
        BluetoothDevice device = BtHeleper.getBluetoothAdapter(mContext).getRemoteDevice(address);
        return device;
    }

    public void startConnect(BesServiceConfig serviceConfig) {
        LOG(TAG, "startConnect: -------");
        mConfig = serviceConfig;
        totauccess = false;
        if (serviceConfig.getDeviceProtocol() == PROTOCOL_USB) {
            connect();
            return;
        }
        BesConnectState besConnectState = getDeviceConnectState(mConfig);
        LOG(TAG, "startConnect: -------" + besConnectState);
        if (mTotaConnectCMD != null && mConfig.getTotaConnect() && besConnectState == BES_CONNECT) {
            LOG(TAG, "startConnect: ----TOTA---BES_CONNECT");
            BTService.refreshConnectListener(mContext, mConfig.getDeviceProtocol(), this, getCurDevice());
            mTotaConnectCMD.setAes_key(BTService.getTotaAesKey(mContext, mConfig.getDeviceProtocol(), getCurDevice()));
            totaConnectSucess();
            return;
        } else if (besConnectState == BES_CONNECT) {
            BTService.refreshConnectListener(mContext, mConfig.getDeviceProtocol(), this, getCurDevice());
            isConnect = true;
            callBackStateChangedMessage(BES_CONNECT_SUCCESS, "");
            onStatusChanged(mConfig.getDevice(), BES_CONNECT_SUCCESS, mConfig.getDeviceProtocol());
            return;
        } else if (besConnectState == BES_CONNECT_TOTA) {
            LOG(TAG, "startConnect: -------DISCONNECT_First");
            BTService.disconnect(mConfig.getDevice());
            new Thread(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                connect();
            }).start();

            return;
        }
        connect();
    }

    private void connect() {
        if (mConfig.getDeviceProtocol() == DeviceProtocol.PROTOCOL_BLE && mConfig.getDevice().getBleAddress() != null) {
            LOG(TAG, "startConnect: -------ble");
            Log.d(TAG, "wangsm, startConnect: -------ble");
            if (mConfig == null) {
                Log.d(TAG, "wangsm, startConnect: -------mConfig is null");
            }
            mDeviceConnector = BleConnector.getsConnector(mContext, this, mConfig);
            mDeviceConnector.connect(mConfig.getDevice(), this);
        } else if (mConfig.getDeviceProtocol() == DeviceProtocol.PROTOCOL_SPP && mConfig.getDevice().getDeviceMAC() != null) {
            mDeviceConnector = SppConnector.getsConnector(mContext, mConfig);
            mDeviceConnector.connect(mConfig.getDevice(), this);
        } else if (mConfig.getDeviceProtocol() == DeviceProtocol.PROTOCOL_GATT_BR_EDR && mConfig.getDevice().getDeviceMAC() != null) {
            mDeviceConnector = BleConnector.getsConnector(mContext, this, mConfig);
            mDeviceConnector.connect(mConfig.getDevice(), this);
        } else if (mConfig.getDeviceProtocol() == PROTOCOL_USB && mConfig.getDevice().getDeviceVid() != null && mConfig.getDevice().getDevicePid() != null) {
            mDeviceConnector = UsbConnector.getsConnector(mContext, mConfig);
            mDeviceConnector.connect(mConfig.getDevice(), this);
        } else {
            callBackErrorMessage(BesSdkConstants.MSG_PARAMETER_ERROR);
        }
    }

    public void disconnected() {
        if (mDeviceConnector != null) {
            mDeviceConnector.disconnect(mConfig.getDevice());
        }
    }

    public DeviceConnector getmDeviceConnector() {
        return mDeviceConnector;
    }

    @Override
    public void onStatusChanged(HmDevice device, int status, DeviceProtocol protocol) {
        LOG(TAG, "onStatusChanged---Base,status: " + status);// 666
        isConnect = status == BesSdkConstants.BES_CONNECT_SUCCESS;
        if (mConfig.getTotaConnect() && !totauccess && status == BesSdkConstants.BES_CONNECT_SUCCESS) {
            sendData(mTotaConnectCMD.totaStartData());
            addTimeOutMsg(mMsgHandler, 5000, BES_NOTI_MSG_TIMEOUT_TOTA_START);
        }
        if (status == BesSdkConstants.BES_CONNECT_ERROR) {
            totauccess = false;
        }
    }

    @Override
    public void onDataReceived(BaseMessage deviceMessage) {
        byte[] data = (byte[]) deviceMessage.getMsgContent();
        // callBackStateChangedMessage(WATCH_AVS_RECEIVE_DATA, "no decode:"+ ArrayUtil.toHex(data));

        if (data[0] == (byte) 0x00 && (data)[1] == (byte) 0x00) {
            return;
        }
        if (userOtaService) {
            if (data[0] == B_9F) {
                byte[] contentData = new byte[data.length - 5];
                System.arraycopy(data, 5, contentData, 0, data.length - 5);
                data = contentData;
            } else {
                LOG(TAG, "onDataReceived isCommandSetFunction: -----" + ArrayUtil.toHex(data));
                return;
            }
        }
        if (mConfig.getTotaConnect() && !totauccess) {
            int result = mTotaConnectCMD.receiveData(mConfig.getUseTotaV2() ? mTotaConnectCMD.setTotaV2PacketData(data, false) : data);
            if (result == BesSdkConstants.BES_TOTA_CONFIRM) {
                removeTimeoutMsg(mMsgHandler, BES_NOTI_MSG_TIMEOUT_TOTA_START);
                sendData(mTotaConnectCMD.totaConfirm());
            } else if (result == BesSdkConstants.BES_TOTA_SUCCESS) {
                totaConnectSucess();
            } else if (result == BesSdkConstants.BES_TOTA_ERROR) {
                totauccess = false;
                callBackTotaConnectState(totauccess);
            }
        } else if (totauccess) {
            // boolean totaEncryption = (boolean) SPHelper.getPreference(mContext,
            // BesSdkConstants.BES_TOTA_ENCRYPTION_KEY, BesSdkConstants.BES_TOTA_ENCRYPTION_VALUE);
            // if (totaEncryption) {
            // deviceMessage.setMsgContent(mConfig.getUseTotaV2() ? mTotaConnectCMD.setTotaV2PacketData(data, true) :
            // mTotaConnectCMD.totaDecodeData(data));
            // }
            if (data[8] == (byte) 0xff && data[9] == (byte) 0x7f) {
                deviceMessage.setMsgContent(mTotaConnectCMD.setTotaV2PacketData(data, false));
            } else {
                boolean totaEncryption = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_TOTA_ENCRYPTION_KEY, BesSdkConstants.BES_TOTA_ENCRYPTION_VALUE);
                if (totaEncryption) {
                    deviceMessage.setMsgContent(mConfig.getUseTotaV2() ? mTotaConnectCMD.setTotaV2PacketData(data, true) : mTotaConnectCMD.totaDecodeData(data));
                }
            }
        }
        mMsgHandler.removeMessages(MSG_TIME_OUT);
    }

    @Override
    public void notifyWrite(int state) {

    }

    public void callBackTotaConnectState(boolean state) {
        if (mBesServiceListener != null) {
            mBesServiceListener.onTotaConnectState(state, mConfig.getDevice());
        }
    }

    public void callBackErrorMessage(int msg) {
        if (mBesServiceListener != null) {
            mBesServiceListener.onErrorMessage(msg, mConfig.getDevice());
        }
    }

    public void callBackErrorMessage(int msg, String params) {
        Log.d(TAG, "callBackErrorMessage msg = " + msg + ",params = " + params);
        if (mBBServiceListener != null) {
            mBBServiceListener.onBBSMessage(msg, params);
        }
    }

    public void callBackStateChangedMessage(int msg, String msgStr) {
        if (mBesServiceListener != null) {
            mBesServiceListener.onStateChangedMessage(msg, msgStr, mConfig.getDevice());
        }
    }

    public void callBackSuccessMessage(int msg) {
        Log.d(TAG, "begin call back success msg = " + msg);
        if (mBesServiceListener != null) {
            Log.d(TAG, "begin call back :onSuccessMessage");
            mBesServiceListener.onSuccessMessage(msg, mConfig.getDevice());
        }
        Log.d(TAG, "begin call back :onSuccessMessage end");
    }

    public void callBackSuccessMessage(int msg, String params) {
        Log.d(TAG, "begin call back success msg and params,msg: " + msg);
        if (mBesServiceListener != null) {
            Log.d(TAG, "begin call back bes :onSuccessMessage");
            mBesServiceListener.onSuccessMessage(msg, mConfig.getDevice());
        }
        if (mBBServiceListener != null) {
            Log.d(TAG, "begin call back bb :onBBSuccessMessage,msg: " + msg);
            mBBServiceListener.onBBSMessage(msg, params);
        }
        Log.d(TAG, "onSuccessMessage params end");
    }

    public boolean sendData(byte[] data) {
        if (!isConnect) {
            Log.d(TAG, "sendData --,isConnect is false,return");
            callBackErrorMessage(BesSdkConstants.BES_CONNECT_ERROR);
            return false;
        }
        if (userOtaService) {
            Log.d(TAG, "sendData --,return sendUseOtaServiceData");
            return sendUseOtaServiceData(data);
        }
        int elpoRunFlag = (int) SPHelper.getPreference(mContext, BesSdkConstants.BES_BLE_KEY_ELPO_RUNNING_FLAG, 0);
        int elpoCmdID = (int) SPHelper.getPreference(mContext, BesSdkConstants.BES_BLE_KEY_ELPO_CMD_ID, 0);
        if (elpoRunFlag == 1 && elpoCmdID != 888) { // elpo running......
            Log.d(TAG, "sendData --,return elpo running flag......" + elpoRunFlag);
            return false;
        }
        //LOG(TAG, "sendData: -----" + ArrayUtil.toHex(data));
        LOG(TAG, "sendData: -----" + data.length);
        Log.d(TAG, "sendData -- gogogo");
        boolean totaEncryption = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_TOTA_ENCRYPTION_KEY, BesSdkConstants.BES_TOTA_ENCRYPTION_VALUE);
        boolean ret = BTService.sendData(mContext, mConfig.getDeviceProtocol(), (totauccess && totaEncryption) ? (mConfig.getUseTotaV2() ? mTotaConnectCMD.getTotaV2Packet(mTotaConnectCMD.totaEncryptData(data)) : mTotaConnectCMD.totaEncryptData(data)) : (mConfig.getUseTotaV2() ? mTotaConnectCMD.getTotaV2Packet(data) : data), getCurDevice());
        Log.d(TAG, "sendData ret = " + ret);
        return ret;
    }

    public boolean sendUseOtaServiceData(byte[] data) {
        LOG(TAG, "sendCommandSetTOTAData: -----" + ArrayUtil.toHex(data));
        LOG(TAG, "sendCommandSetTOTAData: -----" + data.length);
        byte[] param = totauccess ? mTotaConnectCMD.getTotaV2Packet(mTotaConnectCMD.totaEncryptData(data)) : mTotaConnectCMD.getTotaV2Packet(data);
        return BTService.sendData(mContext, mConfig.getDeviceProtocol(), ArrayUtil.byteMerger(ArrayUtil.byteMerger(new byte[]{B_9F}, ArrayUtil.intToBytesLittle(param.length)), param), getCurDevice());
    }

    public boolean sendDataWithoutResponse(byte[] data) {
        if (!isConnect) {
            return false;
        }
        LOG(TAG, "sendDataWithoutResponse: ----" + ArrayUtil.toHex(data));
        LOG(TAG, "sendDataWithoutResponse: -----" + data.length);
        return BTService.sendDataWithoutResponse(mContext, PROTOCOL_BLE, data, getCurDevice());
        /*
         * boolean totaEncryption = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_TOTA_ENCRYPTION_KEY,
         * BesSdkConstants.BES_TOTA_ENCRYPTION_VALUE); return BTService.sendDataWithoutResponse(mContext,
         * mConfig.getDeviceProtocol(), (totauccess && totaEncryption) ? (mConfig.getUseTotaV2() ?
         * mTotaConnectCMD.getTotaV2Packet(mTotaConnectCMD.totaEncryptData(data)) :
         * mTotaConnectCMD.totaEncryptData(data)) : (mConfig.getUseTotaV2() ? mTotaConnectCMD.getTotaV2Packet(data) :
         * data), getCurDevice());
         */
    }

    public boolean sendNoEncryptionDataWithoutResponse(byte[] data) {
        if (!isConnect) {
            return false;
        }
        LOG(TAG, "sendNoEncryptionDataWithoutResponse: ----" + ArrayUtil.toHex(data));
        return BTService.sendDataWithoutResponse(mContext, mConfig.getDeviceProtocol(), mTotaConnectCMD.getTotaV2Packet(data), getCurDevice());
    }

    public boolean sendSppData(byte[] data) {
        if (!isConnect) {
            return false;
        }
        // LOG(TAG, "sendSppData: ----"+ ArrayUtil.toHex(data));
        LOG(TAG, "sendSppData: -----" + data.length);
        return BTService.sendSppData(mContext, mTotaConnectCMD.getTotaV2Packet(mTotaConnectCMD.totaEncryptData(data)));
    }

    public boolean sendData(byte[] data, long millis) {
        addTimeOut(60000);//60 senconds
        return sendData(data);
    }

    public void sendDataDelay(Handler handler, int what, long millis) {
        Message message = handler.obtainMessage(what);
        handler.sendMessageDelayed(message, millis);
    }

    public void sendHandlerData(Handler handler, int what) {
        Message message = handler.obtainMessage(what);
        handler.sendMessage(message);
    }

    public void addTimeOut(long millis) {
        Message message = mMsgHandler.obtainMessage(MSG_TIME_OUT);
        mMsgHandler.sendMessageDelayed(message, millis);
    }

    public void addTimeOutMsg(Handler handler, long millis, int what) {
        Message message = handler.obtainMessage(what);
        handler.sendMessageDelayed(message, millis);
    }

    public void removeTimeoutMsg(Handler handler, int what) {
        handler.removeMessages(what);
    }

    public void LOG(String TAG, String msg) {
        Log.d(TAG, msg);
    }

    public void LOGAAA(String TAG, String msg) {
        Log.i(TAG, "LOG: --" + msg);
        if (mContext == null) {
            return;
        }
        String saveLogName = (String) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_NAME, "");
        boolean saveLog = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_KEY, BesSdkConstants.BES_SAVE_LOG_VALUE);
        if (saveLog) {
            if (saveLogName.equals(BES_SAVE_LOG_OTA)) {
                LogUtils.writeForOTA(TAG, msg, saveLogName);
            } else if (saveLogName.length() > 0) {
                LogUtils.writeForLOG(TAG, msg, saveLogName);
            }
        }
    }

    @Override
    public void notifyWrite(int state, int index) {

    }

    private void totaConnectSucess() {
        isConnect = true;
        totauccess = true;
        BTService.saveTotaAesKey(mContext, mConfig.getDeviceProtocol(), mTotaConnectCMD.getAes_key(), getCurDevice());
        callBackTotaConnectState(totauccess);
    }

    private void totaConnectFail() {
        isConnect = false;
        totauccess = false;
        callBackTotaConnectState(totauccess);
        disconnected();
    }
}
